﻿using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fujitsu.JiraAnalysis.TrackingSystemAdapter;
using Fujitsu.JiraAnalysis.Common;
using Fujitsu.JiraAnalysis.DAL;
using Fujitsu.JiraAnalysis.JiraRPC;
using System.Data.Objects.DataClasses;

namespace Fujitsu.JiraAnalysis.ReplicationService.Synchronize
{
    abstract class DbObjectUpdateBase
    {
        protected UserStorage userStorage = new UserStorage();
        protected IEventLog eventLog = new EventLogWrapper();

        protected ITrackSystemInfo Adapter { get; set; }
        protected StringBuilder logInfo = new StringBuilder();

        private int Id { get; set; }
        protected string Key { get; set; }
        protected string Name { get; set; }

        protected DbObjectUpdateBase(ITrackSystemInfo adapter, int id, string key, string name)
        {
            Adapter = adapter;
            Id = id;
            Key = key;
            Name = name;
        }

        public void Update()
        {
            var remoteEntities = GetRemoteEntities(Key);

            foreach (var remoteEntity in remoteEntities)
            {
                var dbEntityObject = FindMatchInDb(Id, remoteEntity);
                if (dbEntityObject != null)
                {
                    UpdateEntityObject(dbEntityObject, remoteEntity);
                }
                else
                {
                    AddEntityObjectToDb(Id, remoteEntity);
                }
            }

            FindAndDeleteOldData(Id, remoteEntities);
        }

        private void FindAndDeleteOldData(int id, IEnumerable<AbstractRemoteEntity> remoteEntities)
        {
            var toBeDeletedIDs = GetToBeDeletedList(id, remoteEntities);
            foreach (var entityObjectId in toBeDeletedIDs)
            {
                DeleteEntityObject(entityObjectId);
            }
        }

        protected abstract IEnumerable<AbstractRemoteEntity> GetRemoteEntities(string projectKey);

        protected abstract EntityObject FindMatchInDb(int id, AbstractRemoteEntity remoteEntity);

        protected abstract void UpdateEntityObject(EntityObject dbEntityObject, AbstractRemoteEntity remoteEntity);

        protected abstract void AddEntityObjectToDb(int id, AbstractRemoteEntity remoteEntityToBeAdded);

        protected abstract IEnumerable<int> GetToBeDeletedList(int id, IEnumerable<AbstractRemoteEntity> remoteEntities);

        protected abstract void DeleteEntityObject(int dbEntityObjectId);

        protected bool UpdateEntityObjectField<T>(EntityObject dbObject, string fieldName, T currentDbFieldValue, T newDbFieldValue)
        {
            if ((!(currentDbFieldValue == null ^ newDbFieldValue == null)) &&
                ((currentDbFieldValue == null || newDbFieldValue == null) ||
                 (currentDbFieldValue.ToString() == newDbFieldValue.ToString())))
                return false;

            var type = dbObject.GetType();
            var dbProperties = type.GetProperties();

            var dbProperty = dbProperties.First(t => t.Name == fieldName);

            dbProperty.SetValue(dbObject, newDbFieldValue, null);

            logInfo.AppendLine(string.Format("\t{0}", fieldName));

            return true;
        }

        protected int? UpdateRelatedUser(int userId, string jiraUserName)
        {
            var currentUser = userStorage.GetUser(userId);
            if (currentUser != null)
            {
                var currentJiraUser = Adapter.GetUser(currentUser.Login);

                if (currentJiraUser != null)
                {
                    if (currentJiraUser.name != jiraUserName)
                    {
                        var jiraUser = Adapter.GetUser(jiraUserName);
                        return userStorage.AddOrUpdateUser(jiraUser.name, jiraUser.name, jiraUser.fullname);
                    }
                }
            }
            return null;
        }
    }
}
